/**
 * 
 */
package es.esi.gemde.methodmanager.transformations;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Vector;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;

import es.esi.gemde.methodmanager.MethodManagerPlugin;
import es.esi.gemde.methodmanager.methodmodel.method.GEMDEMethod;
import es.esi.gemde.methodmanager.methodmodel.method.MethodPackage;
import es.esi.gemde.modeltransformator.exceptions.TransformationEngineException;
import es.esi.gemde.modeltransformator.service.AbstractJavaTransformation;

/**
 * Implementation of the transformation from a Method Model to a full GEMDE plugin
 * that guides the users though the different method phases and tasks.
 * 
 * @author Adrian Noguero (adrian.noguero@tecnalia.com)
 * @author Guillermo Rodriguez (guillermo.rodriguez@tecnalia.com)
 *
 */
public class MethodModel2Plugintransformation extends
		AbstractJavaTransformation {
	
	// Properties
	private HashMap<String, EClass> inputs;
	
	private String projectName;
	private GEMDEMethod model;
	
	private static final String LINE_SEPARATOR = System.getProperty("line.separator");
	
	public static final String TRANSFORMATION_NAME = "GEMDE Method to Code";
	
	/**
	 * Default constructor of the class. Initializes internal variables.
	 */
	public MethodModel2Plugintransformation() {
		inputs = new HashMap<String, EClass>();
		inputs.put("MODEL", MethodPackage.Literals.GEMDE_METHOD);
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformation#getName()
	 */
	@Override
	public String getName() {
		return TRANSFORMATION_NAME;
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformation#getDescription()
	 */
	@Override
	public String getDescription() {
		return "This transformation generates a plugin implementing the guidelines of a GEMDE method model directly from the code.";
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformation#addTransformationOption(java.lang.String, java.lang.String)
	 */
	@Override
	public void addTransformationOption(String option, String value) {
		// Options are not supported
	}

	/* (non-Javadoc)
	 * @see es.esi.gemde.modeltransformator.service.ITransformation#getOptions()
	 */
	@Override
	public HashMap<String, String> getOptions() {
		return new HashMap<String, String>(); 
	}

	@Override
	public IStatus execute(EObject[] inputs, String outputPath)
			throws IllegalArgumentException, TransformationEngineException {
		if (inputs.length != 1 || !(inputs[0] instanceof GEMDEMethod)) {
			throw new IllegalArgumentException("Provided inputs do not match the requirements of the transformation.");
		}
		
		if (outputPath == null || !outputPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString())) {
			throw new IllegalArgumentException("Provided output folder is not a valid one, this transformation must be generated in the workspace root");
		}
		
		model = (GEMDEMethod) inputs[0];
		projectName = model.getProjectName();
		
		generateCheatSheetProject();
		
		return new Status(IStatus.OK, MethodManagerPlugin.PLUGIN_ID, "Method project generated successfully!");
	}

	@Override
	public List<EClass> getRequiredInputList() {
		Vector<EClass> ret = new Vector<EClass>();
		ret.addAll(inputs.values());
		return ret;
	}

	@Override
	public HashMap<String, EClass> getInputs() {
		LinkedHashMap<String, EClass> ret = new LinkedHashMap<String, EClass>();
		ret.putAll(inputs);
		return ret;
	}

	@Override
	public List<EClass> getProducedOutputList() {
		Vector<EClass> ret = new Vector<EClass>();
		return ret;
	}

	@Override
	public HashMap<String, EClass> getOutputs() {
		LinkedHashMap<String, EClass> ret = new LinkedHashMap<String, EClass>();
		return ret;
	}
	
	// Transformation logic functions by guillermo.rodriguez@tecnalia.com

	private void generateCheatSheetProject() throws TransformationEngineException {
		//Generate the root project folder
		IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
		if(project.exists()) {
			throw new TransformationEngineException("Destination folder should not exist");
		} else {		
			try {
				NullProgressMonitor progressMonitor = new NullProgressMonitor();
				//Create and open the new project
				project.create(progressMonitor);
				project.open(progressMonitor);
				//Add natures
				IProjectDescription description = project.getDescription();
				String[] natures = new String[2];
				natures[0] = "org.eclipse.pde.PluginNature";
				natures[1] = "org.eclipse.jdt.core.javanature";
				description.setNatureIds(natures);
				description.setName(projectName);
				project.setDescription(description, progressMonitor);
				
				//Create files and folders
				generateBuildPropertiesFile(project);
				generateDotClasspathFile(project);
				generatePluignXMLFile(project);
					
				generateMetaInfFolder(project);
				generateDotSettingsFolder(project);				
				generateSrcFolder(project);
				generateBinFolder(project);
				
				//Cheatsheet folder will be generated in other transformation
	
			} catch (CoreException e) {
				e.printStackTrace();
				throw new TransformationEngineException("There where errors creating the new project");
			}
		}
		
	}


	private void generateDotSettingsFolder(IProject project) throws TransformationEngineException {
		// Generate the folder
		final String folderName = ".settings";
		IFolder folder = generateFolder(project, folderName);
		
		// Generate org.eclipse.jdt.core.prefs file
		final String fileName = "org.eclipse.jdt.core.prefs";
		try {
			InputStream is = new ByteArrayInputStream(("eclipse.preferences.version=1"+LINE_SEPARATOR+"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled" + LINE_SEPARATOR + "org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6" + LINE_SEPARATOR + "org.eclipse.jdt.core.compiler.compliance=1.6" + LINE_SEPARATOR + "org.eclipse.jdt.core.compiler.problem.assertIdentifier=error" + LINE_SEPARATOR + "org.eclipse.jdt.core.compiler.problem.enumIdentifier=error" + LINE_SEPARATOR + "org.eclipse.jdt.core.compiler.source=1.6" + LINE_SEPARATOR + "").getBytes("UTF-8"));
			generateFile(folder, fileName, is);
		} catch (UnsupportedEncodingException e) {
			throw new TransformationEngineException("Error with the file encoding");
		}
	}

	private void generateMetaInfFolder(IProject project) throws TransformationEngineException {
		// Generate the folder
		final String folderName = "META-INF";
		IFolder folder = generateFolder(project, folderName);
		// Generate the MANIFEST.MF
		final String fileName = "MANIFEST.MF";
		try {
			InputStream is = new ByteArrayInputStream(("Manifest-Version: 1.0" + LINE_SEPARATOR + "Bundle-ManifestVersion: 2" + LINE_SEPARATOR + "Bundle-Name: GEMDE generated cheatsheet" + LINE_SEPARATOR + "Bundle-SymbolicName: " + projectName + ";singleton:=true" + LINE_SEPARATOR + "Bundle-Version: 1.0.0.qualifier" + LINE_SEPARATOR + "Bundle-Activator: " + projectName + ".Activator" + LINE_SEPARATOR + "Bundle-Vendor: GEMDE generated" + LINE_SEPARATOR + "Require-Bundle: org.eclipse.ui," + LINE_SEPARATOR + " org.eclipse.core.runtime," + LINE_SEPARATOR + "  org.eclipse.core.resources" + LINE_SEPARATOR + "Bundle-RequiredExecutionEnvironment: JavaSE-1.6" + LINE_SEPARATOR + "Bundle-ActivationPolicy: lazy" + LINE_SEPARATOR).getBytes("UTF-8"));
			generateFile(folder, fileName, is);
		} catch (UnsupportedEncodingException e) {
			throw new TransformationEngineException("Error with the file encoding");
		}
		
	}

	private void generateBinFolder(IProject project) throws TransformationEngineException {
		// Generate the folder
		final String folderName = "bin";
		generateFolder(project, folderName);		
	}

	private void generateSrcFolder(IProject project) throws TransformationEngineException {
		String[] splittedName = projectName.split("\\.");
		// Generate the folder structure
		String folderName = "src";
		generateFolder(project, folderName);
		for (int i = 0; i < splittedName.length; i++) {
			folderName = folderName.concat(String.valueOf(IPath.SEPARATOR)).concat(splittedName[i]);
			generateFolder(project, folderName);
		}
		// Generate code files
		String fileName = folderName.concat(String.valueOf(IPath.SEPARATOR)).concat("Activator.java");
		String content = "package " + projectName +";"  + LINE_SEPARATOR + LINE_SEPARATOR
				+ "import org.eclipse.ui.plugin.AbstractUIPlugin;" + LINE_SEPARATOR
				+ "import org.osgi.framework.BundleContext;" + LINE_SEPARATOR + LINE_SEPARATOR
				+ "/** The activator class controls the plug-in life cycle */"  + LINE_SEPARATOR
				+ "public class Activator extends AbstractUIPlugin {"  + LINE_SEPARATOR
				+ "	// The plug-in ID"  + LINE_SEPARATOR
				+ "	public static final String PLUGIN_ID = \"" + projectName +"\"; //$NON-NLS-1$"  + LINE_SEPARATOR
				+ "	// The shared instance"  + LINE_SEPARATOR
				+ "	private static Activator plugin;"  + LINE_SEPARATOR + LINE_SEPARATOR  
				+ "	/** The constructor */" + LINE_SEPARATOR
				+ "	public Activator() {" + LINE_SEPARATOR
				+ "	}" + LINE_SEPARATOR + LINE_SEPARATOR 
				+ "	/**" + LINE_SEPARATOR
				+ "	 * (non-Javadoc)" + LINE_SEPARATOR
				+ "	 * " + LINE_SEPARATOR
				+ "	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)" + LINE_SEPARATOR
				+ "	 */" + LINE_SEPARATOR
				+ "	public void start(BundleContext context) throws Exception {" + LINE_SEPARATOR
				+ "		super.start(context);" + LINE_SEPARATOR
				+ "		plugin = this;" + LINE_SEPARATOR
				+ "	}" + LINE_SEPARATOR + LINE_SEPARATOR
				+ "	/**" + LINE_SEPARATOR
				+ "	 * (non-Javadoc)" + LINE_SEPARATOR
				+ "	 * " + LINE_SEPARATOR
				+ "	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)" + LINE_SEPARATOR
				+ "	 */" + LINE_SEPARATOR
				+ "	public void stop(BundleContext context) throws Exception {" + LINE_SEPARATOR
				+ "		plugin = null;" + LINE_SEPARATOR
				+ "		super.stop(context);" + LINE_SEPARATOR
				+ "	}" + LINE_SEPARATOR+ LINE_SEPARATOR
				+ "	/**" + LINE_SEPARATOR
				+ "	 * Returns the shared instance" + LINE_SEPARATOR
				+ "	 * " + LINE_SEPARATOR
				+ "	 * @return the shared instance" + LINE_SEPARATOR
				+ "	 */" + LINE_SEPARATOR
				+ "	public static Activator getDefault() {" + LINE_SEPARATOR
				+ "		return plugin;" + LINE_SEPARATOR
				+ "	}" + LINE_SEPARATOR
				+ "}";
		
		try {
			InputStream is = new ByteArrayInputStream(content.getBytes("UTF-8"));
			generateFile(project, fileName, is);
		} catch (UnsupportedEncodingException e) {
			throw new TransformationEngineException("Error with the file encoding");
		}
		
	}

	private void generatePluignXMLFile(IProject project) throws TransformationEngineException {
		final String fileName = "plugin.xml";
		try {
			InputStream is = new ByteArrayInputStream(("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + LINE_SEPARATOR + "<?eclipse version=\"3.4\"?>" + LINE_SEPARATOR + " <plugin>" + LINE_SEPARATOR + "  <extension point=\"org.eclipse.ui.cheatsheets.cheatSheetContent\">" + LINE_SEPARATOR + "   <category name=\"GEMDE generated category\" id=\""+projectName+".category\">" + LINE_SEPARATOR + "   </category>" + LINE_SEPARATOR + " <cheatsheet name=\"GEMDE generated cheatsheet\" category=\""+projectName+".category\" contentFile=\"$nl$/cheatsheets/"+model.getInitialPhase().getTitle().toLowerCase().replace(' ', '_')+".xml\" composite=\"true\" id=\""+projectName+".cheatsheetid\">" + LINE_SEPARATOR + "  <description>GEMDE generated cheatsheet description</description>" + LINE_SEPARATOR + "  </cheatsheet>" + LINE_SEPARATOR + " </extension>" + LINE_SEPARATOR + "</plugin>").getBytes("UTF-8"));
			generateFile(project, fileName, is);
		} catch (UnsupportedEncodingException e) {
			throw new TransformationEngineException("Error with the file encoding");
		}
		
	}

	private void generateDotClasspathFile(IProject project) throws TransformationEngineException {
		final String fileName = ".classpath";
		try {
			InputStream is = new ByteArrayInputStream("<?xml version=\"1.0\" encoding=\"UTF-8\"?><?eclipse version=\"3.4\"?><classpath><classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6\"/><classpathentry kind=\"con\" path=\"org.eclipse.pde.core.requiredPlugins\"/><classpathentry kind=\"src\" path=\"src\"/><classpathentry kind=\"output\" path=\"bin\"/></classpath>".getBytes("UTF-8"));
			generateFile(project, fileName, is);
		} catch (UnsupportedEncodingException e) {
			throw new TransformationEngineException("Error with the file encoding");
		}			
	}

	private void generateBuildPropertiesFile(IProject project) throws TransformationEngineException {
		final String fileName = "build.properties";		
		try {
			InputStream is = new ByteArrayInputStream(("output.. = bin/" + LINE_SEPARATOR + "bin.includes = META-INF/,\\" + LINE_SEPARATOR + "               .,\\"+ LINE_SEPARATOR +"               cheatsheets/,\\"+ LINE_SEPARATOR + "               plugin.xml" + LINE_SEPARATOR + "source.. = src/").getBytes("UTF-8"));
			generateFile(project, fileName, is);
		} catch (UnsupportedEncodingException e) {
			throw new TransformationEngineException("Error with the file encoding");
		}				
	}

	private void generateFile(IProject project, String filename, InputStream content) throws TransformationEngineException {
		IFile file = project.getFile(filename);		
		try {
			InputStream is = content;
			file.create( is, true, new NullProgressMonitor());
		} catch (CoreException e) {
			throw new TransformationEngineException("Error generating "+filename);
		}
		
	}

	private void generateFile(IFolder folder, String filename, InputStream content) throws TransformationEngineException {
		IFile file = folder.getFile(filename);		
		try {
			InputStream is = content;
			file.create( is, true, new NullProgressMonitor());
		} catch (CoreException e) {
			throw new TransformationEngineException("Error generating "+filename);
		}
		
	}

	private IFolder generateFolder(IProject project, String folderName) throws TransformationEngineException {
		IFolder folder = project.getFolder(folderName);		
		try {
			folder.create(true,  true, new NullProgressMonitor());
		} catch (CoreException e) {
			throw new TransformationEngineException("Error generating "+folderName);
		}		
		return folder;
	}

}
